import { AudioClip, AudioSource, Component, director, Node } from 'cc';
import { BaseManager } from '../../base/BaseManager';
import { BaseGlobal } from '../../base/BaseModule';

export enum SoundState {
    None,
    Loading,
    Playing,
    Pausing
}

export class SoundSource extends Component {
    public audioSource: AudioSource | null = null;
    public moduleName: string | null = null;
    public groupName: string | null = null;
    public soundName: string | null = null;
    public soundId: number = 0;
    public soundState: SoundState = SoundState.None;
    public volume = 1;

    public clear(): void {
        this.audioSource.stop();
        this.audioSource = null;
        this.moduleName = null;
        this.groupName = null;
        this.soundName = null;
        this.soundId = 0;
        this.soundState = SoundState.None;
        this.volume = 1;
    }
}

export class SoundManager extends BaseManager {
    static SoundState = SoundState;
    static SoundSource = SoundSource;

    private static _soundContainer: Node | null = null;
    private static get soundContainer(): Node {
        if (this._soundContainer) {
            return this._soundContainer;
        }
        this._soundContainer = new Node('SoundManager');
        director.getScene()?.addChild(this._soundContainer);
        director.addPersistRootNode(this._soundContainer);
        return this._soundContainer;
    }

    // 全局
    private static audioClipCacheMap: Map<string, AudioClip> = new Map();

    // 音乐
    private static musicSource: SoundSource | null = null;
    private static _musicVolumeScale: number = 1; // 音乐音量缩放，取值范围0-1
    /** 音乐音量缩放 */
    public static get musicVolumeScale(): number {
        return this._musicVolumeScale;
    }
    public static set musicVolumeScale(value: number) {
        this._musicVolumeScale = value;
        // 应用到音乐源
        if (this.musicSource && this.musicSource.audioSource) {
            this.musicSource.audioSource.volume = this.musicSource.volume * value;
        }
    }

    // 音效
    private static effectId: number = 1;
    private static effectSources: SoundSource[] = [];
    private static effectIntervalMap: Map<string, number> = new Map();
    private static _effectVolumeScale: number = 1; // 音效音量缩放，取值范围0-1
    /** 音效音量缩放 */
    public static get effectVolumeScale(): number {
        return this._effectVolumeScale;
    }
    public static set effectVolumeScale(value: number) {
        this._effectVolumeScale = value;
        // 应用到所有音效源
        for (const soundSource of this.effectSources) {
            if (soundSource.audioSource) {
                soundSource.audioSource.volume = soundSource.volume * value;
            }
        }
    }

    /**
     * 释放缓存
     */
    public release(): void {
        // 先停止播放中的音频
        if (SoundManager.musicSource && SoundManager.musicSource.moduleName === this.module.moduleName) {
            SoundManager.musicSource.clear();
        }
        for (const soundSource of SoundManager.effectSources) {
            if (soundSource.moduleName === this.module.moduleName) {
                soundSource.clear();
            }
        }

        // 再清理缓存
        const musicKey = this.getMusicName('');
        const effectKey = this.getEffectName('');
        for (const [key] of SoundManager.audioClipCacheMap) {
            if (key.startsWith(musicKey) || key.startsWith(effectKey)) {
                SoundManager.audioClipCacheMap.delete(key);
            }
        }
        for (const [key] of SoundManager.effectIntervalMap) {
            if (key.startsWith(effectKey)) {
                SoundManager.effectIntervalMap.delete(key);
            }
        }
    }

    //#region Music
    private getMusicName(name: string): string {
        return `${this.module.moduleName}:Music:${name}`;
    }

    /**
     * 加载音乐
     * @param name Global-Sound/Music或Module-Sound/Music下的音频文件路径
     */
    public loadMusic(name: string, onCompleted?: (clip: AudioClip | null) => void, onProgress?: (progress: number) => void): void {
        const soundKey = this.getMusicName(name);
        if (SoundManager.audioClipCacheMap.has(soundKey)) {
            const clip = SoundManager.audioClipCacheMap.get(soundKey);
            setTimeout(() => {
                onCompleted?.(clip);
            });
            return;
        }

        const assetPath = this.module instanceof BaseGlobal
            ? `global-sound/music/${name}`
            : `module-sound/music/${name}`;

        this.module.loadAsset(assetPath, AudioClip, (audio) => {
            if (!audio) {
                console.error(`${this.module.moduleName} failed to load audio ${assetPath}`);
                onCompleted?.(audio);
                return;
            }
            SoundManager.audioClipCacheMap.set(soundKey, audio);
            onCompleted?.(audio);
        }, onProgress);
    }

    /**
     * 预加载音乐
     * @param name Global-Sound/Music或Module-Sound/Music下的音频文件路径
     */
    public preloadMusic(name: string, onCompleted?: () => void, onProgress?: (progress: number) => void): void {
        this.loadMusic(name, () => onCompleted?.(), onProgress);
    }

    /**
     * 播放音乐
     * @param name Global-Sound/Music或Module-Sound/Music下的音频文件路径
     */
    public playMusic(name: string, volume = 1, onCompleted?: () => void, onProgress?: (progress: number) => void): void {
        this.loadMusic(name, (clip) => {
            if (!clip) {
                return;
            }

            if (SoundManager.musicSource && (SoundManager.musicSource.soundName !== name || SoundManager.musicSource.moduleName !== this.module.moduleName)) {
                return;
            }

            if (SoundManager.musicSource) {
                SoundManager.musicSource.audioSource?.stop();
            } else {
                const node = new Node('Music');
                node.parent = SoundManager.soundContainer;
                SoundManager.musicSource = new SoundSource();
                SoundManager.musicSource.audioSource = node.addComponent(AudioSource);
            }

            SoundManager.musicSource.soundId = 0;
            SoundManager.musicSource.volume = volume;
            SoundManager.musicSource.soundName = name;
            SoundManager.musicSource.moduleName = this.module.moduleName;
            SoundManager.musicSource.audioSource.clip = clip;
            SoundManager.musicSource.audioSource.loop = true;
            SoundManager.musicSource.audioSource.volume = volume * SoundManager.musicVolumeScale;
            // SoundManager.musicSource.audioSource.mute = false;
            SoundManager.musicSource.audioSource.playOnAwake = false;
            SoundManager.musicSource.audioSource.play();

            onCompleted?.();
        }, onProgress);
    }

    /**
     * 停止音乐
     */
    public stopMusic(): void {
        if (SoundManager.musicSource?.audioSource) {
            SoundManager.musicSource.audioSource.stop();
        }
    }

    /**
     * 暂停音乐
     */
    public pauseMusic(): void {
        if (SoundManager.musicSource?.audioSource) {
            SoundManager.musicSource.audioSource.pause();
        }
    }

    /**
     * 恢复音乐
     */
    public resumeMusic(): void {
        if (SoundManager.musicSource?.audioSource) {
            SoundManager.musicSource.audioSource.play();
        }
    }
    //#endregion

    //#region Effect
    private getEffectName(name: string): string {
        return `${this.module.moduleName}:Effect:${name}`;
    }

    /**
     * 加载音效
     * @param name Global-Sound/Effect或Module-Sound/Effect下的音频文件路径
     */
    public loadEffect(name: string, onCompleted?: (clip: AudioClip | null) => void, onProgress?: (progress: number) => void): void {
        const soundKey = this.getEffectName(name);

        // 查询缓存
        if (SoundManager.audioClipCacheMap.has(soundKey)) {
            const clip = SoundManager.audioClipCacheMap.get(soundKey);
            setTimeout(() => {
                onCompleted?.(clip);
            });
            return;
        }

        const assetPath = this.module instanceof BaseGlobal
            ? `global-sound/effect/${name}`
            : `module-sound/effect/${name}`;

        this.module.loadAsset(assetPath, AudioClip, (audio) => {
            if (!audio) {
                console.error(`${this.module.moduleName} failed to load audio ${assetPath}`);
                onCompleted?.(audio);
                return;
            }
            SoundManager.audioClipCacheMap.set(soundKey, audio);
            onCompleted?.(audio);
        }, onProgress);
    }

    /**
     * 预加载音效
     * @param name Global-Sound/Effect或Module-Sound/Effect下的音频文件路径
     */
    public preloadEffect(name: string, onCompleted?: () => void, onProgress?: (progress: number) => void): void {
        this.loadEffect(name, () => onCompleted?.(), onProgress);
    }

    private createEffect(id: number, clip: AudioClip, name: string, group: string | null, loop = false, volume = 1, onEnd?: () => void): void {
        const moduleName = this.module.moduleName;
        let soundSource: SoundSource | null = null;

        // 查询是否有可复用的节点
        for (const source of SoundManager.effectSources) {
            if (source.audioSource.playing == false) {
                soundSource = source;
                break;
            }
        }

        if (!soundSource) {
            // 创建新节点
            const node = new Node('Effect');
            node.parent = SoundManager.soundContainer;
            soundSource = node.addComponent(SoundSource);
            soundSource.audioSource = node.addComponent(AudioSource);
            SoundManager.effectSources.push(soundSource);
        }

        soundSource.soundId = id;
        soundSource.volume = volume;
        soundSource.groupName = group;
        soundSource.soundName = name;
        soundSource.moduleName = moduleName;

        soundSource.audioSource.clip = clip;
        soundSource.audioSource.loop = loop;
        soundSource.audioSource.volume = volume * SoundManager.effectVolumeScale;
        // soundSource.audioSource.mute = false;
        soundSource.audioSource.playOnAwake = false;
        soundSource.audioSource.play();

        if (onEnd) {
            setTimeout(() => {
                onEnd();
            }, clip.getDuration() * 1000);
        }
    }

    /**
     * 播放音频
     * @param module Module
     * @param group 分组
     * @param name Global-Sound/Effect或Module-Sound/Effect下的音频文件路径
     * @param interval 播放时间间隔(单位:秒)
     * @returns 音频Id(从1开始)
     */
    public playEffectWithGroup(group: string | null, name: string, interval: number = 0, loop = false, volume = 1, onEnd?: () => void): number {
        const soundKey = this.getEffectName(name);
        const soundId = SoundManager.effectId++;

        if (SoundManager.audioClipCacheMap.has(soundKey)) {
            const clip = SoundManager.audioClipCacheMap.get(soundKey);
            const nowMs = Date.now();

            // 正在播放中，不允许重复播放
            if (!SoundManager.effectIntervalMap.has(soundKey) || nowMs >= SoundManager.effectIntervalMap.get(soundKey)) {
                // 间隔时间大于0，设置下次播放时间
                if (interval > 0) {
                    SoundManager.effectIntervalMap.set(soundKey, nowMs + interval * 1000);
                }
                this.createEffect(soundId, clip, name, group, loop, volume, onEnd);
            }
        } else {
            this.loadEffect(name, (clip) => {
                if (!clip) return;

                const nowMs = Date.now();
                if (!SoundManager.effectIntervalMap.has(soundKey) || nowMs >= SoundManager.effectIntervalMap.get(soundKey)) {
                    if (interval > 0) {
                        SoundManager.effectIntervalMap.set(soundKey, nowMs + interval * 1000);
                    }
                    this.createEffect(soundId, clip, name, group, loop, volume, onEnd);
                }
            });
        }

        return soundId;
    }

    /**
     * 播放音频
     * @param name Global-Sound/Effect或Module-Sound/Effect下的音频文件路径
     * @param interval 播放时间间隔(单位:秒)
     * @returns 音频Id(从1开始)
     */
    public playEffect(name: string, interval: number = 0, loop = false, volume = 1, onEnd?: () => void): number {
        return this.playEffectWithGroup(null, name, interval, loop, volume, onEnd);
    }

    /**
     * 停止音频
     * @param id 音频Id
     */
    public stopEffect(id: number): void {
        for (const soundSource of SoundManager.effectSources) {
            if (soundSource.audioSource?.playing && soundSource.soundId === id) {
                soundSource.audioSource.stop();
                return;
            }
        }
    }

    /**
     * 暂停音频
     * @param id 音频Id
     */
    public pauseEffect(id: number): void {
        for (const soundSource of SoundManager.effectSources) {
            if (soundSource.audioSource?.playing && soundSource.soundId === id) {
                soundSource.audioSource.pause();
                return;
            }
        }
    }

    /**
     * 恢复音频
     * @param id 音频Id
     */
    public resumeEffect(id: number): void {
        for (const soundSource of SoundManager.effectSources) {
            if (!soundSource.audioSource?.playing && soundSource.soundId === id) {
                soundSource.audioSource.play();
                return;
            }
        }
    }

    /**
     * 停止音频
     * @param name Sound name
     */
    public stopEffects(name: string): void {
        for (const soundSource of SoundManager.effectSources) {
            if (soundSource.audioSource?.playing && soundSource.soundName === name) {
                soundSource.audioSource.stop();
            }
        }
    }

    /**
     * 暂停音频
     * @param name Sound name
     */
    public pauseEffects(name: string): void {
        for (const soundSource of SoundManager.effectSources) {
            if (soundSource.audioSource?.playing && soundSource.soundName === name) {
                soundSource.audioSource.pause();
            }
        }
    }

    /**
     * 恢复音频
     * @param name Sound name
     */
    public resumeEffects(name: string): void {
        for (const soundSource of SoundManager.effectSources) {
            if (!soundSource.audioSource?.playing && soundSource.soundName === name) {
                soundSource.audioSource.play();
            }
        }
    }

    /**
     * 根据组停止音效
     * @param group Group name
     */
    public stopEffectsByGroup(group: string): void {
        for (const soundSource of SoundManager.effectSources) {
            if (soundSource.audioSource?.playing && soundSource.groupName === group) {
                soundSource.audioSource.stop();
            }
        }
    }

    /**
     * 根据组名暂停音效
     * @param group Group name
     */
    public pauseEffectsByGroup(group: string): void {
        for (const soundSource of SoundManager.effectSources) {
            if (soundSource.audioSource?.playing && soundSource.groupName === group) {
                soundSource.audioSource.pause();
            }
        }
    }

    /**
     * 根据组名恢复音效
     * @param group Group name
     */
    public resumeEffectsByGroup(group: string): void {
        for (const soundSource of SoundManager.effectSources) {
            if (!soundSource.audioSource?.playing && soundSource.groupName === group) {
                soundSource.audioSource.play();
            }
        }
    }

    /**
     * 停止所有音频
     */
    public stopAllEffects(): void {
        for (const soundSource of SoundManager.effectSources) {
            if (soundSource.audioSource?.playing) {
                soundSource.audioSource.stop();
            }
        }
    }

    /**
     * 暂停所有音频
     */
    public pauseAllEffects(): void {
        for (const soundSource of SoundManager.effectSources) {
            if (soundSource.audioSource?.playing) {
                soundSource.audioSource.pause();
            }
        }
    }

    /**
     * 恢复所有音频
     */
    public resumeAllEffects(): void {
        for (const soundSource of SoundManager.effectSources) {
            if (!soundSource.audioSource?.playing) {
                soundSource.audioSource?.play();
            }
        }
    }
    //#endregion
}
